#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from Utils.Timer import timer
from Utils.Logger import logger
from collections import defaultdict
from Src.ExcelParser import ExcelParser


class ConfigExcelParser(ExcelParser):
    """提供解析配置表的特殊方法"""
    @timer(tracer=False)
    def __init__(self, file_path):
        super(ConfigExcelParser, self).__init__(file_path)
        self.attributes_dict = self.load_excel()

    @timer(tracer=False)
    def load_excel(self):
        """
        遍历excel表格的sheet页，再遍历每一行，如果任意行的第一个单元格的内容是'>>XPath'，则说明这一行是属性名称，
        如果第一个单元格的内容包含'>>'字符且内容不是'>>XPath'，则说明这一行是属性值。
        :return: 一键多值字典，键是工作表名称，值包括表格的sheet名，所在行，属性名和属性值对应的字典
        """
        wb = self.workbook
        attr_list, val_list, attr_dic = [], [], {}
        for ws_name in self.worksheet_names:
            dic_val_list = []
            dic_key = ws_name
            ws = wb[ws_name]
            for row_num, c_row in enumerate(ws.iter_rows()):
                tag = c_row[0].value
                if tag == '>>XPath':
                    attr_list = ['SheetName', 'RowNum']
                    for cell in c_row:
                        attr_list.append(cell.value)
                elif tag == '>>Commentary':
                    pass
                elif tag and '>>' in tag:
                    val_list = [ws_name, row_num + 1]
                    for cell in c_row:
                        val_list.append(cell.value)
                    dic = dict(map(lambda k, v: (k, v), attr_list, val_list))
                    # 过滤dict中的空值
                    dic = dict(filter(lambda t: t[1] is not None, dic.items()))
                    dic_val_list.append(dic)
            attr_dic[dic_key] = dic_val_list
        logger.debug('解析工作簿[{0}]成功'.format(self.file_path))
        return attr_dic

    @timer(tracer=False)
    def get_xpath_list(self, sheet_names=None):
        """
        获取工作簿/工作表所有的xpath，如果不填写sheet_names，则获取工作簿，否则则获取相应sheet_names的xpath
        :param sheet_names: 工作表名称，默认为None
        :return: 一键多值字典，键是工作表名称，值包括该工作表内所有出现过的xpath
        """
        xpath_dic = defaultdict(list)
        if isinstance(sheet_names, str):
            ws_names = [sheet_names]
        elif isinstance(sheet_names, list):
            ws_names = sheet_names
        else:
            ws_names = self.worksheet_names

        _attributes_dict = dict(filter(lambda t: t[0] in ws_names, self.attributes_dict.items()))

        for ws_name in _attributes_dict:
            if ws_name not in ws_names:
                _attributes_dict.pop(ws_name)

        for ws_name, attributes_list in _attributes_dict.items():
            for attributes in attributes_list:
                if attributes['>>XPath'] == '>>Commentary':
                    print(attributes)
                if attributes['>>XPath'] not in xpath_dic[ws_name]:
                    xpath_dic[ws_name].append(attributes['>>XPath'])

        return xpath_dic

    @timer(tracer=True)
    def get_column_titles(self, sheet_names=None):
        """
        获取工作簿/工作表的表头，如果不填写sheet_names，则获取工作簿内所有表的表头，
        如果填写sheet_names，则获取相应sheet_names的表头
        :param sheet_names: 工作表名称，默认为None
        :return: 一键多值字典，键是工作表名称，值包括表头组成的列表，如果有多个表头，则有多个列表
        """
        titles_dic = defaultdict(list)
        if isinstance(sheet_names, str):
            ws_names = [sheet_names]
        elif isinstance(sheet_names, list):
            ws_names = sheet_names
        else:
            ws_names = self.worksheet_names
        for ws_name in ws_names:
            _ws = self.get_worksheet(ws_name)
            try:
                for c_row in _ws.iter_rows():
                    li = []
                    if c_row[0].value == '>>XPath':
                        for cell in c_row:
                            if cell.value is not None:
                                li.append(cell.value)
                    if li:
                        titles_dic[ws_name].append(li)
            except AttributeError:
                logger.error('工作簿[{0}]的工作表[{1}]读取出错'.format(self.file_name, ws_name))
        return titles_dic

    @timer(tracer=True)
    def insert_data(self, sheet_name, row, data_list, above_row=False, mark=True):
        """
        将数据插入指定行
        :param sheet_name: 工作表名称组成的列表
        :param row: 行数
        :param data_list: 数据字典组成的列表
        :param above_row: True为行前插入，False为行后插入
        :param mark: True将插入的行背景标记为红色，False则不着色
        :return: 要插入数据组成的嵌套列表
        """
        data_nums = len(data_list)
        _ws = self.get_worksheet(sheet_name)
        titles_list = self.get_column_titles(sheet_names=[sheet_name])[sheet_name]
        titles = list(filter(lambda x: x[0] <= row, titles_list))[-1]
        datas_list = []
        for data in data_list:
            datas = []
            for title in titles[1:]:
                if title in data:
                    datas.append(data[title])
                else:
                    datas.append('')
            datas_list.append(datas)
        c_row = row if above_row else row + 1
        _ws.insert_rows(c_row, data_nums)
        for datas in datas_list:
            for index, data in enumerate(datas):
                _ws.cell(row=c_row, column=index + 1).value = data
                if mark:
                    self.set_cell_background_color(sheet_name, c_row, index + 1, 'FF0000')
            c_row += 1
        logger.debug('工作簿[{0}]的工作表[{1}]插入数据成功'.format(self.file_name, sheet_name))
        return datas_list

    @timer(tracer=False)
    def search_data(self, xpath, sheet_name=None, **kwargs):
        """
        搜索表格中满足条件的某几行
        :param xpath: 工作表中的xpath值
        :param sheet_name: 工作表的名称，默认为None，如果不填写则默认搜索整个工作簿
        :param kwargs: 关键字参数，由表格中的属性名称和属性值组成，可填写多个
        :return: 字典形式的行内容，包括文件名，工作表名称，行数，以及工作表中的属性值
        """
        result_list = []
        if isinstance(sheet_name, str):
            result_list = self.attributes_dict[sheet_name]
        elif isinstance(sheet_name, list):
            for _sheet_name in sheet_name:
                result_list = result_list + self.attributes_dict[_sheet_name]
        else:
            for val in self.attributes_dict.values():
                result_list = result_list + list(val)
        result_list = list(filter(lambda _dic: _dic['>>XPath'] == xpath, result_list))
        if kwargs:
            for key, val in kwargs.items():
                result_list = list(filter(lambda _dic: key in _dic, result_list))
                result_list = list(filter(lambda _dic: _dic[key] == val, result_list))
        return result_list

    @timer(tracer=True)
    def sr_specific_result(self, xpath, sheet_name=None, choose_result='First', **kwargs):
        """
        搜索表格中满足条件的某几行，返回指定行
        :param xpath: 工作表中的xpath值
        :param sheet_name: 工作表的名称，默认为None，如果不填写则默认搜索整个工作簿
        :param choose_result: 'First'返回搜索到的第一行，'Last'返回搜索到的最后一行，'All'返回全部结果
        :param kwargs: 关键字参数，由表格中的属性名称和属性值组成，可填写多个
        :return: 字典形式的行内容，包括文件名，工作表名称，行数，以及工作表中的属性值
        """
        result_list = self.search_data(xpath, sheet_name, **kwargs)
        if result_list:
            logger.debug('搜索工作簿[{0}]成功'.format(self.file_path))
            if choose_result == 'Last':
                return result_list[-1]
            elif choose_result is 'All':
                return result_list
            else:
                return result_list[0]
        else:
            logger.debug('搜索工作簿[{0}]结果为空'.format(self.file_path))
            return None

    @timer(tracer=True)
    def sr_title_nonadjacent(self, xpath, title, sheet_name=None, choose_result='First', **kwargs):
        """
        搜索表格中满足条件的某几行，返回某一数字属性不连续的行
        :param xpath: 工作表中的xpath值
        :param title: 工作表中想要搜索的列的表头
        :param sheet_name: 工作表的名称，默认为None，如果不填写则默认搜索整个工作簿
        :param choose_result: 'First'返回搜索到的第一行，'Last'返回搜索到的最后一行，'All'返回全部结果
        :param kwargs: 关键字参数，由表格中的属性名称和属性值组成，可填写多个
        :return: 字典形式的行内容，包括文件名，工作表名称，行数，以及工作表中的属性值
        """
        _result_list = self.search_data(xpath, sheet_name, **kwargs)
        result_list = []
        try:
            num = int(_result_list[0][title])
            for result in _result_list[1:]:
                _num = int(result[title])
                if abs(_num) - abs(num) != 1:
                    result_list.append(result)
                num = _num
        except ValueError:
            logger.error('参数[{0}]类型错误，请确保它是整数'.format(title))
        except KeyError:
            logger.error('参数名[{0}]不存在，请检查表头'.format(title))
        if result_list:
            logger.debug('搜索工作簿[{0}]成功'.format(self.file_path))
            if choose_result == 'Last':
                return result_list[-1]
            elif choose_result is 'All':
                return result_list
            else:
                return result_list[0]
        else:
            logger.debug('搜索工作簿[{0}]结果为空，取最后一行'.format(self.file_path))
            return _result_list[-1]

    @timer(tracer=True)
    def sr_between_range(self, xpath, title, start_range, end_range, sheet_name=None, choose_result='First', **kwargs):
        """
        搜索表格中满足条件的某几行，返回在某一区间范围内的行
        :param xpath: 工作簿中的xpath值
        :param title: 工作表中想要搜索的列的表头
        :param start_range: 区间起始值
        :param end_range: 区间结束值
        :param sheet_name: 工作表的名称，默认为None，如果不填写则默认搜索整个工作簿
        :param choose_result: 'First'返回搜索到的第一行，'Last'返回搜索到的最后一行，'All'返回全部结果
        :param kwargs: 关键字参数，由表格中的属性名称和属性值组成，可填写多个
        :return: 字典形式的行内容，包括文件名，工作表名称，行数，以及工作表中的属性值
        """
        _result_list = self.search_data(xpath, sheet_name, **kwargs)
        result_list = []
        try:
            for result in _result_list:
                num = int(result[title])
                if start_range <= num <= end_range:
                    result_list.append(result)
        except ValueError:
            logger.error('参数[{0}]类型错误，请确保它是整数'.format(title))
        except KeyError:
            logger.error('参数[{0}]不存在，请检查表头'.format(title))
        except TypeError:
            logger.error('参数[{0}, {1}]类型错误，请确保它是整数'.format(start_range, end_range))
        if result_list:
            logger.debug('搜索工作簿[{0}]成功'.format(self.file_path))
            if choose_result == 'Last':
                return result_list[-1]
            elif choose_result is 'All':
                return result_list
            else:
                return result_list[0]
        else:
            logger.debug('搜索工作簿[{0}]结果为空，取最后一行'.format(self.file_path))
            return _result_list[-1]


if __name__ == '__main__':
    from Config.PackPath import CONFIG_EXCEL_DIR
    import os
    logger.info('开始')
    path = os.path.join(CONFIG_EXCEL_DIR, 'ShopConfig.xlsx')
    excel = ConfigExcelParser(path)
    a = excel.get_xpath_list(sheet_names=['Sheet2', 'Sheet3'])
    for _k, _v in a.items():
        print(_k, _v)
    logger.info('结束')
